/*
 * File Name:               UIManager.cs
 *
 * Description:             UI逻辑管理器
 * Author:                  lisiyu <576603306@qq.com>
 * Create Date:             2016/11/22
 */

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class UIManager
{
    public static readonly int UnityUILayer = LayerMask.NameToLayer("UI");
    public static readonly int UnityUIMask = LayerMask.GetMask("UI", "UIPlayer");
    public static readonly int UIResolutionWidth = 1280;
    public static readonly int UIResolutionHeight = 720;
    public static readonly float PlaneDistance = 50f;
    public static readonly float PlaneOffsetDistance = 30f;
    public static readonly int PlaneMaxCount = 10;
    public static readonly int cameraFieldOfView = 36;

    private int mOldScreenWidth;
    private int mOldScreenHeight;

    private List<CanvasScaler> mCanvasScalers;
    public enum ELayer
    {
        Active = 1,       // 活动层（处理人物能量条之类）
        HUD = 2,          // HUD层 (场景常驻根UI)
        Popup = 3,        // 弹框层（大部分功能在此）
        Guide = 4,        // 新手引导层
        Loading = 5,      // loading层
        Notice = 6,       // 提示信息层（公告tip等提示信息）
    }

    /// <summary>
    /// UI使用单件模式的坏处是容易在一些不当的地方被调用 影响逻辑清晰程度 
    /// </summary>
    private static UIManager mSingleton;

    public static UIManager Singleton
    {
        get
        {
            if (mSingleton == null)
            {
                mSingleton = new UIManager();
                mSingleton.onInit();
            }
            return mSingleton;
        }
    }

    /// <summary>
    /// 适配因数 
    /// </summary>
    private float mAspectFactor;

    /// <summary>
    /// 摄像机
    /// </summary>
    private Camera mCamera;

    /// <summary>
    /// 已经打开的窗口 
    /// </summary>
    private Dictionary<int, List<UIWindow>> mOpendWindows;

    /// <summary>
    /// 各个层级的Root节点 
    /// </summary>
    private Transform[] mLayersTrans;

    /// <summary>
    /// 依赖组群名称列表 
    /// </summary>
    private Dictionary<int, List<string>> mGroupNames;

    /// <summary>
    /// 打开中窗口的具体依赖关系 
    /// </summary>
    private Dictionary<string, UIGroup> mGroupMaps;

    /// <summary>
    /// UI事件
    /// </summary>
    private EventSystem mEventSystem;

    /// <summary>
    /// 根节点
    /// </summary>
    private GameObject _rootObj;

    /// <summary>
    /// 创建对应序号的画布
    /// </summary>
    /// <param name="_rootObj"></param>
    /// <param name="index"></param>
    private void createCanvas(GameObject _rootObj, int index)
    {
        var _canvasObj = new GameObject(string.Format("[Canvas_{0}]", index));
        _canvasObj.transform.SetParent(_rootObj.transform, false);
        _canvasObj.layer = UnityUILayer;
        var _canvas = _canvasObj.AddComponent<Canvas>();
        _canvas.renderMode = RenderMode.ScreenSpaceCamera;
        _canvas.worldCamera = mCamera;
        _canvas.planeDistance = PlaneDistance + Mathf.CeilToInt((PlaneMaxCount - index) * (PlaneMaxCount - index - 1) * PlaneOffsetDistance * 0.5f);
        _canvas.sortingOrder = index;

        var _canvasScaler = _canvasObj.AddComponent<CanvasScaler>();
        _canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        _canvasScaler.referenceResolution = new Vector2(UIResolutionWidth, UIResolutionHeight);
        _canvasObj.AddComponent<GraphicRaycaster>();
        // 强制更新一次canvas属性
        _canvasScaler.SendMessage("Update");
        mCanvasScalers.Add(_canvasScaler);

        mLayersTrans[index] = _canvasObj.transform;
    }

    /// <summary>
    /// 初始化代码 
    /// </summary>
    private void onInit()
    {
        mOpendWindows = new Dictionary<int, List<UIWindow>>(PlaneMaxCount);
        foreach (ELayer layer in Enum.GetValues(typeof(ELayer)))
        {
            mOpendWindows[(int)layer] = new List<UIWindow>(PlaneMaxCount);
        }
        mLayersTrans = new Transform[PlaneMaxCount];
        mGroupNames = new Dictionary<int, List<string>>(PlaneMaxCount);
        mGroupMaps = new Dictionary<string, UIGroup>(PlaneMaxCount);
        mCanvasScalers = new List<CanvasScaler>(PlaneMaxCount);

        // 根节点
        _rootObj = new GameObject("[UIRoot]");
        GameObject.DontDestroyOnLoad(_rootObj);

        // 摄像机
        var _cameraObj = new GameObject("[UICamera]");
        _cameraObj.transform.SetParent(_rootObj.transform, false);
        _cameraObj.layer = UnityUILayer;
        mCamera = _cameraObj.AddComponent<Camera>();
        mCamera.clearFlags = CameraClearFlags.Depth;
        mCamera.cullingMask = UnityUIMask;
        mCamera.fieldOfView = cameraFieldOfView;
        mCamera.nearClipPlane = PlaneDistance - PlaneOffsetDistance;
        mCamera.farClipPlane = PlaneDistance + Mathf.CeilToInt(PlaneOffsetDistance * PlaneMaxCount * (PlaneMaxCount - 1) * 0.5f);
        mCamera.depth = 5;

        // 画布
        for (int i = 0; i < PlaneMaxCount; i++)
        {
            createCanvas(_rootObj, i);
        }

        autoAspect();

        // 事件
        var _evtObj = new GameObject("[EventSystem]");
        _evtObj.transform.SetParent(_rootObj.transform, false);
        mEventSystem = _evtObj.AddComponent<EventSystem>();
        _evtObj.AddComponent<StandaloneInputModule>();
    }

    /// <summary>
    /// 自动适配
    /// </summary>
    private void autoAspect()
    {
        if (mOldScreenWidth == Screen.width && mOldScreenHeight == Screen.height)
            return;

        var _matchWidthOrHeight = 0f;
        // 动态适配比例
        var _scale = 1f * Screen.width / Screen.height;
        // 参考分辨率 1152*768,1.5
        if (_scale > UIResolutionWidth / UIResolutionHeight)
        {   // 1280*720,1.777; 800*480,1.666
            _matchWidthOrHeight = 1;
        }
        else
        {   // 1024*768,1.333
            _matchWidthOrHeight = 0;
        }
        this.mAspectFactor = (1 - _matchWidthOrHeight) * UIResolutionWidth / Screen.width + _matchWidthOrHeight * UIResolutionHeight / Screen.height;

        mOldScreenWidth = Screen.width;
        mOldScreenHeight = Screen.height;

        foreach (var scaler in mCanvasScalers)
        {
            scaler.matchWidthOrHeight = _matchWidthOrHeight;
        }
    }

    /// <summary>
    /// 层级排序
    /// </summary>
    private void sortLayer()
    {
        int index = 0;
        foreach (ELayer layer in Enum.GetValues(typeof(ELayer)))
        {
            foreach (var item in mOpendWindows[(int)layer])
            {
                if (item.mWidth == null)
                    item.setParentTransform(mLayersTrans[index++]);
                else
                {
                    item.setParentTransform(mLayersTrans[index]);
                    index += item.mWidth.width;
                }
            }
        }
    }

    /// <summary>
    /// 打开制定窗口 
    /// </summary>
    /// <param name="winName"> Window name. </param>
    /// <param name="args">    Arguments. </param>
    /// <typeparam name="T"> The 1st type parameter. </typeparam>
    public void open<T>(string winName, object args = null, bool isPreload = false) where T : UIWindow, new()
    {
        if (isOpen(winName))
        {
            return;
        }

        var clone = ResourceManager.Instance.LoadObjectSync<GameObject>(winName);//GameObject.Instantiate(Resources.Load<GameObject>(winName));

        if (isPreload)
        {
            GameObject.Destroy(clone);
        }
        else
        {
            var win = XCachePool.Singleton.pop<T>();

            // 层级控制
            var _layer = clone.GetComponent<UILayer>();
            if (_layer == null)
            {
                Debug.LogError(winName + "窗口的层级未设置");
            }

            // 依赖控制 - 互斥关系
            var _group = clone.GetComponent<UIGroup>();
            if (_group != null)
            {
                // 互斥
                if (_group.pMutexGroups != null)
                {
                    var list = new List<string>();      // todo 内存碎片 待优化
                    foreach (var tmp in _group.pMutexGroups)
                    {
                        if (!mGroupNames.ContainsKey(tmp))
                            continue;
                        foreach (var name in mGroupNames[tmp])
                        {
                            list.Add(name);
                        }
                    }
                    foreach (var tmp in list)
                    {
                        close(tmp);
                    }
                }

                // 自身
                if (!mGroupNames.ContainsKey(_group.pGroup))
                    mGroupNames.Add(_group.pGroup, new List<string>());
                mGroupNames[_group.pGroup].Add(winName);
                mGroupMaps.Add(winName, _group);
            }

            win.onCreated(winName, clone, args);

            mOpendWindows[(int)_layer.pLayer].Add(win);

            autoAspect();
            sortLayer();

            win.onOpened();

            mEventSystem.enabled = false;
            XTimeManager.Singleton.delayCall(0.5f, (obj) => mEventSystem.enabled = true);
        }
    }

    public void lateUpdate()
    {
#if UNITY_EDITOR && !ENABLE_DEEP_PROFILER
        if (mOldScreenWidth != Screen.width)
        {
            autoAspect();
        }
#endif

        foreach (var layer in mOpendWindows)
        {
            foreach (var item in layer.Value)
            {
                item.LateUpdate();
            }
        }
    }

    public void CloseAll()
    {
        var closeList = new List<string>();
        foreach (var layer in mOpendWindows)
        {
            foreach (var item in layer.Value)
            {
                closeList.Add(item.WinName);
            }
        }

        closeList.ForEach(w => close(w));
    }

    /// <summary>
    /// 关闭制定窗口 
    /// </summary>
    /// <param name="winName"> Window name. </param>
    public void close(string winName)
    {
        var win = getOpendWindow(winName);
        if (win == null)
            return;

        // 依赖控制 - 依赖关系
        if (mGroupMaps.ContainsKey(winName))
        {
            var _group = mGroupMaps[winName];
            // 自身
            mGroupNames[_group.pGroup].Remove(winName);
            mGroupMaps.Remove(winName);

            // 依赖
            if (_group.pDependGroups != null)
            {
                var list = new List<string>();
                foreach (var tmp in _group.pDependGroups)
                {
                    if (!mGroupNames.ContainsKey(tmp))
                        continue;
                    foreach (var name in mGroupNames[tmp])
                    {
                        list.Add(name);
                    }
                }
                foreach (var tmp in list)
                {
                    close(tmp);
                }
            }
        }

        mOpendWindows[(int)win.pLayer].Remove(win);
        XCachePool.Singleton.push(win);
    }

    /// <summary>
    /// 判断某个窗口是否已打开 
    /// </summary>
    /// <returns> <c> true </c>, if open was ised, <c> false </c> otherwise. </returns>
    /// <param name="winName"> Window name. </param>
    public bool isOpen(string winName)
    {
        foreach (var layer in mOpendWindows)
        {
            foreach (var item in layer.Value)
            {
                if (item.WinName == winName)
                    return true;
            }
        }
        return false;
    }

    public UIWindow getOpendWindow(string winName)
    {
        foreach (var layer in mOpendWindows)
        {
            foreach (var item in layer.Value)
            {
                if (item.WinName == winName)
                    return item;
            }
        }
        return null;
    }

    public void showOrHideWindow(bool show, string winName)
    {
        var win = getOpendWindow(winName);
        if (win == null)
            return;

        win.showOrHide(show);
    }

    /// <summary>
    /// 屏幕坐标到UI坐标转换 
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public Vector3 ScreenToUIPoint(Vector3 pos)
    {
        var ret = Vector3.zero;
        ret.x = (pos.x - Screen.width * 0.5f) * mAspectFactor;
        ret.y = (pos.y - Screen.height * 0.5f) * mAspectFactor;
        return ret;
    }

    /// <summary>
    /// UI坐标到屏幕坐标 
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public Vector3 UIToScreenPoint(Vector3 pos)
    {
        var ret = Vector3.zero;
        ret.x = pos.x / mAspectFactor + Screen.width * 0.5f;
        ret.y = pos.y / mAspectFactor + Screen.height * 0.5f;
        return ret;
    }

    /// <summary>
    /// 世界坐标到UI坐标 
    /// </summary>
    /// <param name="pos">   </param>
    /// <param name="camera"></param>
    /// <returns></returns>
    public Vector3 WorldToUIPoint(Vector3 pos, Camera camera = null)
    {
        if (camera == null)
            camera = mCamera;
        if (camera == null)
            return Vector2.zero;

        return ScreenToUIPoint(camera.WorldToScreenPoint(pos));
    }

    /// <summary>
    /// 屏幕坐标到世界坐标
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public Vector3 ScreenToWorldPoint(Vector2 pos, float z, Camera camera = null)
    {
        if (camera == null)
            camera = mCamera;
        if (camera == null)
            return Vector2.zero;
        //pos.z = PlaneDistance;

        return camera.ScreenToWorldPoint(new Vector3(pos.x, pos.y, z));
    }

    /// <summary>
    /// 屏幕坐标到世界坐标
    /// </summary>
    /// <param name="pos"></param>
    /// <returns></returns>
    public Vector3 WorldToScreenPoint(Vector3 pos, Camera camera = null)
    {
        if (camera == null)
            camera = mCamera;
        if (camera == null)
            return Vector2.zero;
        //pos.z = PlaneDistance;

        return camera.WorldToScreenPoint(pos);
    }

    /// <summary>
    /// 启用输入或者禁止输入
    /// </summary>
    /// <param name="enable"></param>
    public void enableInput(bool enable)
    {
        mEventSystem.enabled = enable;
        if (enable)
        {
            _rootObj.BroadcastMessage("OnEnableInput", SendMessageOptions.DontRequireReceiver);
        }
        else
        {
            _rootObj.BroadcastMessage("OnDisableInput", SendMessageOptions.DontRequireReceiver);
        }
    }
}